package org.dromara.system.kernel;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.BeanUtils;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.system.domain.bo.OrderRelateBo;
import org.dromara.system.domain.bo.node.NodePushUpdateBo;
import org.dromara.system.kernel.channel.*;
import org.dromara.system.constants.OrderConstants;
import org.dromara.system.domain.*;
import org.dromara.system.domain.bo.PayGroupTypeQueryBo;
import org.dromara.system.domain.bo.aparams.AOrderBo;
import org.dromara.system.domain.bo.config.ApiConfig;
import org.dromara.system.domain.bo.cparams.OrderProduct;
import org.dromara.system.domain.bo.cparams.StripeAuthBo;
import org.dromara.system.domain.vo.*;
import org.dromara.system.mapper.*;
import org.dromara.system.service.*;
import org.dromara.system.utils.RequestUtils;
import org.dromara.system.utils.SendEmailUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 订单插入核心处理
 */
@RefreshScope
@Service
@RequiredArgsConstructor
public class OrderInsertKernel {

    private final IPayGroupAccountService payGroupAccountService;
    private final PayGroupTypeMapper payGroupTypeMapper;
    private final PayTypeMapper payTypeMapper;
    private final PayBlackMapper payBlackMapper;
    private final IPayOrderService orderService;
    private final IPayRateService payRateService;
    private final IPayGroupTypeService payGroupTypeService;
    private final IPayAccountService accountService;
    private final IPayOrderLogService orderLogService;
    private final PayAppKernel payAppKernel;
    private final OrderUpdateKernel orderUpdateKernel;
    private final ConeOrderKernel coneOrderKernel;
    private final XiangPayOrderKernel xiangPayOrderKernel;
    private final CompanyChannel2Kernel companyChannel2Kernel;
    private final CompanyChannel4Kernel companyChannel4Kernel;
    private final CompanyChannel999Kernel companyChannel999Kernel;
    private final PayPolicyKernel payPolicyKernel;
    private final PayGroupKernel payGroupKernel;
    private final AdyenKernel adyenKernel;
    private final PowerKernel powerKernel;
    private final SysUserMapper sysUserMapper;
    private final RedisAlgorithmKernel redisAlgorithmKernel;

    // 创建一个固定大小的线程池
    private static final ExecutorService executor = Executors.newFixedThreadPool(2);
    @Value("${ipCountry.url}")
    private String ipCountryUrl;
    @Value("${ipCountry.key}")
    private String ipCountryKey;
    @Value("${hideOrder}")
    public Boolean hideOrder;
    @Value("${fixedKeyTime}")
    public Integer fixedKeyTime;
    @Value("${gatewayDomain}")
    public String gatewayDomain;
    @Value("${pluginName}")
    public String pluginName;
    @Value("${openCommission}")
    private Boolean openCommission;

    public R<?> pushOrder(AOrderBo bo, String secretKey) {
        //新增一条订单日志
        PayOrderLog log = orderLogService.saveLog(null, null, generateOrderNumber(), bo.getOrderAid(), "推送订单", JSONObject.toJSONString(bo, JSONWriter.Feature.WriteMapNullValue), null, System.currentTimeMillis());
        if (log == null) {
            return R.fail("Failed to add order log");
        }
        bo.setOrderNo(log.getOrderNo());
        R<PushOrderVo> result = pushOrder(bo, secretKey, log);

        PushOrderVo pushVo = result.getData();
        PayOrder order = null;
        ApiConfig.PayMethod method = null;
        JSONObject nodeParams = null;
        if (pushVo != null) {
            order = pushVo.getOrder();
            method = pushVo.getPayMethod();
            nodeParams = pushVo.getNodeParams();
        }

        JSONObject data = new JSONObject().fluentPut("innerLink", "").fluentPut("link", "");
        R<?> res;
        if (R.isSuccess(result) && order != null) {
            data.put("orderNo", order.getOrderNo());
            data.put("createTime", order.getCreateTime());
            data.put("checkoutId", StringUtils.isEmpty(order.getLink()) ? order.getSessionId() : "");
            if (method != null && Objects.equals(method.getInnerType(), 3)) {
                data.put("innerLink", order.getLink());
            } else {
                data.put("link", order.getLink());
            }

            //获取节点请求参数
            if (nodeParams != null) {
                res = R.ok(new JSONObject().fluentPut("nodeParams", nodeParams).fluentPut("returnParams", data).fluentPut("innerType", method != null ? method.getInnerType() : null));
            } else {
                res = R.ok(data);
            }
        } else {
            res = R.fail(result.getMsg());
        }

        orderLogService.updateLog(log.getLogId(), order != null ? order.getOrderId() : null, JSONObject.toJSONString(res, JSONWriter.Feature.WriteMapNullValue), System.currentTimeMillis() - log.getTime());
        return res;
    }

    /**
     * 推送订单
     *
     * @param bo        订单推送参数
     * @param appSecret 应用密匙
     * @param log       日志对象
     * @return 订单信息
     */
    public R<PushOrderVo> pushOrder(AOrderBo bo, String appSecret, PayOrderLog log) {

        SysUser agent = null;
        SysUser user = null;
        PayOrder order = null;
        try {
            //查询应用
            R<PayApp> rApp = payAppKernel.getPayApp(bo.getAppId(), appSecret);
            if (R.isError(rApp)) {
                return R.fail(rApp.getMsg());
            }
            PayApp app = rApp.getData();
            if (Objects.equals(bo.getXtPlugin(), pluginName) && app.getAgentId() != null) {
                //校验此应用是否能接入YY插件
                agent = sysUserMapper.selectById(app.getAgentId());
                if (agent != null && !Objects.equals(agent.getNexpay(), 1)) {
                    return R.fail("This app is not allowed to access the plugin");
                }
            }

            //抽佣额度校验
            if (openCommission) {
                if (agent == null) {
                    agent = sysUserMapper.selectById(app.getAgentId());
                }
                if (agent == null || agent.getCommissionLimit() == null || agent.getCommissionLimit().compareTo(BigDecimal.ZERO) <= 0
                    || agent.getCommissionLimit().compareTo(agent.getCommissionMoney()) <= 0) {
                    return R.fail("This admin has been credit limited");
                }
                user = sysUserMapper.selectById(app.getClientId());
                if (user == null || user.getCommissionLimit() == null || user.getCommissionLimit().compareTo(BigDecimal.ZERO) <= 0
                    || user.getCommissionLimit().compareTo(user.getCommissionMoney()) <= 0) {
                    return R.fail("This client has been credit limited");
                }
            }

            //校验订单数据
            String msg = verifyOrderData(bo, app);
            if (StringUtils.isNotEmpty(msg)) {
                return R.fail(msg);
            }

            //获取汇率
            BigDecimal rates = new BigDecimal(1);
            if (!bo.getCurrency().equals("USD")) {
                PayRate payRate = payRateService.selectByCurrency(bo.getCurrency());
                if (payRate == null) {
                    return R.fail("No rate info");//没有获取到汇率信息
                }
                rates = new BigDecimal(payRate.getValue());
            }
            //设置美元金额
            bo.setUsdMoney(bo.getTotal().divide(rates, 2, RoundingMode.HALF_UP));
            bo.setRates(rates);

            //如果没传账号,则轮询收款账号
            if (bo.getGroupAccountId() == null) {
                R<?> r = loopGroupAccount(bo, app);
                if (R.isError(r)) {
                    return R.fail(r.getMsg());
                }
            }

            //获取轮询组账号信息
            R<PayOrderInsertAccountVo> res = getGroupAccount(bo, app);
            if (R.isError(res)) {
                return R.fail(res.getMsg());
            }
            PayGroupTypeVo groupTypeVo = res.getData().getGroupTypeVo();
            PayGroupAccount groupAccount = res.getData().getPayGroupAccount();
            PayAccount account = res.getData().getPayAccount();
            PayType payType = res.getData().getPayType();
            String fixedKey = res.getData().getFixedKey();

            //是否强制货币类型
            BigDecimal forceRates = null;
            if (Objects.equals(groupAccount.getIsForce(), 1) && !bo.getCurrency().equals(groupAccount.getForceCurrency())) {
                //获取强制货币汇率
                PayRate payRate = payRateService.selectByCurrency(groupAccount.getForceCurrency());
                if (payRate == null) {
                    return R.fail("No rate info!");//没有获取到汇率信息
                }
                forceRates = new BigDecimal(payRate.getValue());
            }


            //获取API配置
            ApiConfig config = ApiConfig.getConfig(payType.getConfig(), account.getApiType());

            //根据平台走单独校验
            msg = verifyOrderDataV2(bo, payType);
            if (StringUtils.isNotEmpty(msg)) {
                return R.fail(msg);
            }

            //新增订单
            order = insertPayOrder(app, payType, groupAccount, account, bo, forceRates, groupTypeVo);

            //高风险订单校验
            String riskDetail = heightRiskHandle(app, groupAccount, account, bo, order, groupTypeVo);
            if (StringUtils.isNotEmpty(riskDetail)) {
                //更新订单
                PayOrder up = new PayOrder();
                up.setOrderId(order.getOrderId());
                up.setStatus(4);//风险订单
                up.setRiskDetail(riskDetail);
                orderService.updatePayOrderAsync(up);
                return R.fail("This is a high-risk order", new PushOrderVo(order));
            }

            //获取支付链接
            R<PayOrder> r;
            OrderRelateBo relateBo = new OrderRelateBo(order, app, payType, account, groupAccount, log, bo);
            if (Objects.equals(payType.getCode(), "cq-1")) {
                //CQ-1获取支付链接处理
                r = coneOrderKernel.requestPayLink(account, groupAccount, order, log);
            } else if (Objects.equals(payType.getCode(), "adyen")) {
                // adyen支付
                r = adyenKernel.requestPayLink(relateBo);
            } else if (Objects.equals(payType.getCode(), "power")) {
                // power支付
                r = powerKernel.requestPayLink(relateBo);
            } else if (Objects.equals(payType.getCode(), "companychannel-1")) {
                //XiangPay获取支付链接处理
                r = xiangPayOrderKernel.requestPayLink(account, groupAccount, order, log);
            } else if (Objects.equals(payType.getCode(), "companychannel-2")) {
                //CompanyChannel2获取支付链接处理
                r = companyChannel2Kernel.requestPayLink(account, groupAccount, order, log);
            } else if (Objects.equals(payType.getCode(), "companychannel-4")) {
                //CompanyChannel4获取支付链接处理
                r = companyChannel4Kernel.requestPayLink(relateBo);
            } else if (Objects.equals(payType.getCode(), "companychannel-999")) {
                //CompanyChannel999获取支付链接处理
                r = companyChannel999Kernel.requestPayLink(relateBo);
            } else if (Objects.equals(bo.getVersion(), "node")){
                //节点插件获取请求参数
                return requestParams(relateBo, config, fixedKey);
            } else {
                //默认到PHP插件获取支付链接
                r = requestPayLink(account, groupAccount, order, log);
            }

            return linkResult(r, order, account, groupAccount, payType, config, fixedKey);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("Order processing exception", new PushOrderVo(order));
        } finally {
//            System.out.println("【总耗时】执行时间： " + (System.currentTimeMillis() - startTime) + " 毫秒");
        }
    }

    /**
     * 处理支付链接结果
     *
     * @param r            支付链接结果
     * @param order        订单信息
     * @param account      收款账号信息
     * @param groupAccount 收款组账号信息
     * @param payType      支付类型信息
     * @param config       API配置信息
     * @param fixedKey     防关联key
     * @return 处理结果
     */
    public R<PushOrderVo> linkResult(R<PayOrder> r, PayOrder order, PayAccount account, PayGroupAccount groupAccount, PayType payType, ApiConfig config, String fixedKey) {
        //获取链接失败
        if (R.isError(r)) {
            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setStatus(0);
            orderService.updatePayOrderAsync(up);

            //如果有防关联key,则删除防关联key
            if (StringUtils.isNotEmpty(fixedKey)) {
                RedisUtils.deleteObject(fixedKey);
            }

            //检查轮询组收款账号失败次数
            checkGroupAccountFailNum(groupAccount);

            //处理封禁提示
            R<?> r1 = orderUpdateKernel.accountBanDeal(BeanUtils.copy(payType, PayTypeVo.class), account, r.getMsg());
            if (R.isError(r1)) {
                return R.fail(r1.getMsg(), new PushOrderVo(order));
            }

            return R.fail(r.getMsg(), new PushOrderVo(order));
        }

        //异步处理是否需要主动获取订单状态
        orderUpdateKernel.sendGetStatusHandel(order.getOrderId(), config);

        //处理是否需要延时自动失败
        orderUpdateKernel.sendOrderFail(order.getOrderId(), config);

        //获取支付方式配置
        ApiConfig.PayMethod method = ApiConfig.getPayMethod(config, groupAccount.getPayMethod());

        return R.ok(new PushOrderVo(order, method));
    }

    /**
     * 推送订单
     *
     * @param bo        订单推送参数
     * @param appSecret 应用密匙
     * @param log       日志对象
     * @return 订单信息
     */
    public R<PushOrderVo> pushOrder2(AOrderBo bo, String appSecret, PayOrderLog log) {
        if (StringUtils.isEmpty(bo.getPayType())) {
            return R.fail("payType is empty");
        }

        PayOrder order = null;
        try {
            //查询应用
            R<PayApp> rApp = payAppKernel.getPayApp(bo.getAppId(), appSecret);
            if (R.isError(rApp)) {
                return R.fail(rApp.getMsg());
            }
            PayApp app = rApp.getData();

            //校验订单数据
            String msg = verifyOrderData(bo, app);
            if (StringUtils.isNotEmpty(msg)) {
                return R.fail(msg);
            }

            //获取汇率
            BigDecimal rates = new BigDecimal(1);
            if (!bo.getCurrency().equals("USD")) {
                PayRate payRate = payRateService.selectByCurrency(bo.getCurrency());
                if (payRate == null) {
                    return R.fail("No rate info");//没有获取到汇率信息
                }
                rates = new BigDecimal(payRate.getValue());
            }
            //设置美元金额
            bo.setUsdMoney(bo.getTotal().divide(rates, 2, RoundingMode.HALF_UP));
            bo.setRates(rates);

            //轮询收款账号
            R<PayOrderInsertAccountVo> res = payGroupKernel.loopPayGroupAccount(bo, app);
            if (R.isError(res)) {
                return R.fail(res.getMsg());
            }
            PayGroupTypeVo groupTypeVo = res.getData().getGroupTypeVo();
            PayGroupAccount groupAccount = res.getData().getPayGroupAccount();
            PayAccount account = res.getData().getPayAccount();
            PayType payType = res.getData().getPayType();
            String fixedKey = res.getData().getFixedKey();

            //是否强制货币类型
            BigDecimal forceRates = null;
            if (Objects.equals(groupAccount.getIsForce(), 1) && !bo.getCurrency().equals(groupAccount.getForceCurrency())) {
                //获取强制货币汇率
                PayRate payRate = payRateService.selectByCurrency(groupAccount.getForceCurrency());
                if (payRate == null) {
                    return R.fail("No rate info!");//没有获取到汇率信息
                }
                forceRates = new BigDecimal(payRate.getValue());
            }

            //获取API配置
            ApiConfig config = ApiConfig.getConfig(payType.getConfig(), account.getApiType());

            //根据平台走单独校验
            msg = verifyOrderDataV2(bo, payType);
            if (StringUtils.isNotEmpty(msg)) {
                return R.fail(msg);
            }

            //新增订单
            order = insertPayOrder(app, payType, groupAccount, account, bo, forceRates, groupTypeVo);

            //高风险订单校验
            String riskDetail = heightRiskHandle(app, groupAccount, account, bo, order, groupTypeVo);
            if (StringUtils.isNotEmpty(riskDetail)) {
                //更新订单
                PayOrder up = new PayOrder();
                up.setOrderId(order.getOrderId());
                up.setStatus(4);//风险订单
                up.setRiskDetail(riskDetail);
                orderService.updatePayOrderAsync(up);
                return R.fail("This is a high-risk order", new PushOrderVo(order));
            }

            //获取支付链接
            R<PayOrder> r;
            OrderRelateBo relateBo = new OrderRelateBo(order, app, payType, account, groupAccount, log, bo);
            if (Objects.equals(payType.getCode(), "cq-1")) {
                //CQ-1获取支付链接处理
                r = coneOrderKernel.requestPayLink(account, groupAccount, order, log);
            } else if (Objects.equals(payType.getCode(), "adyen")) {
                //adyen 支付
                r = adyenKernel.requestPayLink(relateBo);
            } else if (Objects.equals(payType.getCode(), "power")) {
                // power支付
                r = powerKernel.requestPayLink(relateBo);
            } else if (Objects.equals(payType.getCode(), "companychannel-1")) {
                //XiangPay获取支付链接处理
                r = xiangPayOrderKernel.requestPayLink(account, groupAccount, order, log);
            } else if (Objects.equals(payType.getCode(), "companychannel-2")) {
                //CompanyChannel2获取支付链接处理
                r = companyChannel2Kernel.requestPayLink(account, groupAccount, order, log);
            } else {
                //默认到PHP插件获取支付链接
                r = requestPayLink(account, groupAccount, order, log);
            }

            //获取链接失败
            if (R.isError(r)) {
                //更新订单
                PayOrder up = new PayOrder();
                up.setOrderId(order.getOrderId());
                up.setStatus(0);
                orderService.updatePayOrderAsync(up);

                //如果有防关联key,则删除防关联key
                if (StringUtils.isNotEmpty(fixedKey)) {
                    RedisUtils.deleteObject(fixedKey);
                }

                //处理封禁提示
                R<?> r1 = orderUpdateKernel.accountBanDeal(BeanUtils.copy(payType, PayTypeVo.class), account, r.getMsg());
                if (R.isError(r1)) {
                    return R.fail(r1.getMsg(), new PushOrderVo(order));
                }

                return R.fail(r.getMsg(), new PushOrderVo(order));
            }

            //异步处理是否需要主动获取订单状态
            orderUpdateKernel.sendGetStatusHandel(order.getOrderId(), config);

            //处理是否需要延时自动失败
            orderUpdateKernel.sendOrderFail(order.getOrderId(), config);

            //获取支付方式配置
            ApiConfig.PayMethod method = ApiConfig.getPayMethod(config, groupAccount.getPayMethod());

            return R.ok(new PushOrderVo(order, method));
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("Order processing exception", new PushOrderVo(order));
        } finally {
//            System.out.println("【总耗时】执行时间： " + (System.currentTimeMillis() - startTime) + " 毫秒");
        }
    }

    /**
     * 获取支付链接
     *
     * @param account      收款账号
     * @param groupAccount 轮询组收款账号配置
     * @param order        订单信息
     */
    private R<PayOrder> requestPayLink(PayAccount account, PayGroupAccount groupAccount, PayOrder order, PayOrderLog log) {

        //获取账号请求地址
        String baseUrl = accountService.buildRequestUrl(account);
        if (StringUtils.isEmpty(baseUrl)) {
            return R.fail("pluginUrl is invalid", order);
        }
        //请求地址
        String url = baseUrl + "pay";
        Boolean isNewAccount = Boolean.FALSE;
        if (StringUtils.isNotEmpty(account.getPlugin())) {
            url += "/";
            isNewAccount = Boolean.TRUE;
        }

        //构建参数
        JSONObject params = buildParams(groupAccount, account, order, isNewAccount);
        if (params == null) {
            return R.fail("no config params found", order);
        }

        //请求
        long time = System.currentTimeMillis();
        String res = new RequestUtils().doPostJsonByToken(url, params.toJSONString());

        //记录请求日志
        params.fluentPut("requestUrl", url);
        orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "获取支付链接", params.toJSONString(), res, log != null ? System.currentTimeMillis() - time : System.currentTimeMillis());

        //处理返回结果
        return linkUpdateOrder(res, order, log);
    }

    /**
     * 处理返回结果
     *
     * @param res  返回结果
     * @param order 订单信息
     * @param log  日志信息
     * @return 订单信息
     */
    public R<PayOrder> linkUpdateOrder(String res, PayOrder order, PayOrderLog log) {
        try {
            if (StringUtils.isEmpty(res)) {
                return R.fail("Request link null", order);
            }

            //解析返回参数
            R<?> r;
            try {
                r = JSONObject.parseObject(res, R.class);
            } catch (Exception e) {
                return R.fail(res, order);
            }

            if (R.isError(r)) {
                return R.fail(r.getMsg(), order);
            }

            JSONObject data = (JSONObject) r.getData();
            String payUrl = data.getString("url");
            String sessionId = data.getString("session_id");
            String terminal_order_sn = data.getString("terminal_order_sn");

            //写到订单对象里，返出去使用
            order.setLink(payUrl);
            order.setSessionId(sessionId);

            //更新订单
            PayOrder up = new PayOrder();
            up.setOrderId(order.getOrderId());
            up.setLink(payUrl);
            up.setSessionId(sessionId);
            up.setOrderSn(terminal_order_sn);
            orderService.updatePayOrderAsync(up);

            //记录更新日志
            orderLogService.saveLog(log != null ? log.getLogId() : null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "更新B端订单信息", JSONObject.toJSONString(up), "success", 10L);

            return R.ok(order);

        } catch (Exception e) {
            return R.fail("Request link fail", order);
        }
    }

    /**
     * 获取支付请求参数
     *
     * @param relateBo 订单信息
     * @param config   支付配置
     * @return 支付请求参数
     */
    private R<PushOrderVo> requestParams(OrderRelateBo relateBo, ApiConfig config, String fixedKey) {

        PayAccount account = relateBo.getAccount();
        PayGroupAccount groupAccount = relateBo.getGroupAccount();
        PayOrder order = relateBo.getOrder();
        PayOrderLog log = relateBo.getLog();

        //获取账号请求地址
        String baseUrl = accountService.buildRequestUrl(account);
        if (StringUtils.isEmpty(baseUrl)) {
            return R.fail("pluginUrl is invalid");
        }
        //请求地址
        String url = baseUrl + "pay";
        Boolean isNewAccount = Boolean.FALSE;
        if (StringUtils.isNotEmpty(account.getPlugin())) {
            url += "/";
            isNewAccount = Boolean.TRUE;
        }

        //构建参数
        JSONObject params = buildParams(groupAccount, account, order, isNewAccount);
        if (params == null) {
            return R.fail("no config params found");
        }

        JSONObject json = new JSONObject();
        json.put("orderId", order.getOrderId());
        json.put("fixedKey", fixedKey);
        json.put("logId", log != null ? log.getLogId() : null);
        json.put("url", url);
        json.put("params", params);

        //获取支付方式配置
        ApiConfig.PayMethod method = ApiConfig.getPayMethod(config, groupAccount.getPayMethod());

        //处理返回结果
        return R.ok(new PushOrderVo(order, json, method));
    }

    /**
     * 节点更新订单
     *
     * @param bo 订单推送参数
     * @return 订单信息
     */
    public R<?> nodeUpdate(NodePushUpdateBo bo) {
        PayOrder order = orderService.selectById(bo.getOrderId());
        if (order == null) {
            return R.fail("order is null");
        }
       PayAccount account = accountService.selectById(order.getAccountId());
       PayGroupAccount groupAccount = payGroupAccountService.queryById(order.getGroupAccountId());
       PayType payType = payTypeMapper.selectById(order.getPayTypeId());

        //记录更新日志
       PayOrderLog log = orderLogService.saveLog(null, order.getOrderId(), order.getOrderNo(), order.getOrderAid(), "节点异步更新订单", JSONObject.toJSONString(bo), bo.getRes(), bo.getUseTime());

        //处理返回结果，更新订单
       R<PayOrder> r = linkUpdateOrder(bo.getRes(), order, log);

       //处理支付链接结果
      return linkResult(r, order, account, groupAccount, payType, ApiConfig.getConfig(payType.getConfig(), account.getApiType()), bo.getFixedKey());
    }

    /**
     * 高风险订单校验
     *
     * @param groupAccount 轮询组账号配置
     * @param bo           订单参数
     * @return 返回风险细节
     */
    private String heightRiskHandle(PayApp app, PayGroupAccount groupAccount, PayAccount account, AOrderBo bo, PayOrder order, PayGroupTypeVo groupTypeVo) {

        //验证国家是否在黑名单
        if (bo.getBilling() != null && StringUtils.isNotEmpty(bo.getBilling().getCountry())) {
            String country = bo.getBilling().getCountry();

            if (StringUtils.isNotEmpty(groupAccount.getBlackCountry())
                && Arrays.asList(groupAccount.getBlackCountry().split(",")).contains(country)) {
                return "黑名单: 国家 " + country;
            }

            //风险策略 国家校验
            if (groupTypeVo.getPolicyId() != null) {
                R<String> r = payPolicyKernel.validateCountry(groupTypeVo.getPolicyId(), country);
                if (R.isError(r)) {
                    return "黑名单: 国家 " + country + ".";
                }
            }

            //验证收款账号国家策略
            if (account != null && account.getCountryType() != null && StringUtils.isNotEmpty(account.getCountryCode())) {
                R<String> result = payPolicyKernel.validateCountry(account.getCountryType(), account.getCountryCode(), country);
                if (R.isError(result)) {
                    return "黑名单: 国家 " + country + ".";
                }
            }
        }

        //检查是否小于单笔订单限额
        BigDecimal money = bo.getUsdMoney();
        if (money != null && money.compareTo(BigDecimal.ZERO) > 0
            && groupAccount.getMinMoney() != null && groupAccount.getMinMoney().compareTo(BigDecimal.ZERO) > 0
            && money.compareTo(groupAccount.getMinMoney()) < 0) {
            return "订单金额：小于最小订单金额";
        }

        //检查是否大于单笔订单限额
        if (money != null && money.compareTo(BigDecimal.ZERO) > 0
            && groupAccount.getMaxMoney() != null && groupAccount.getMaxMoney().compareTo(BigDecimal.ZERO) > 0
            && money.compareTo(groupAccount.getMaxMoney()) > 0) {
            return "订单金额：大于最大订单金额";
        }

        //风险单判断
        if (bo.getCreateLink() == null && app.getPolicyId() != null) {
            String str = payPolicyKernel.validateOrderFields(app.getPolicyId(), order);
            if (StringUtils.isNotEmpty(str)) {
                return str;
            }
        }

        //黑名单IP  邮箱
        List<String> vals = new ArrayList<>();
        if (StringUtils.isNotEmpty(bo.getCustomerIpAddress())) {
            vals.add(bo.getCustomerIpAddress());
        }
        if (bo.getBilling() != null && StringUtils.isNotEmpty(bo.getBilling().getEmail())) {
            vals.add(bo.getBilling().getEmail());
        }
        if (vals.isEmpty()) {
            return null;
        }
        LambdaQueryWrapper<PayBlack> query = new LambdaQueryWrapper<PayBlack>().eq(PayBlack::getCreateId, app.getAgentId());
        query.and(wrapper -> vals.forEach(val -> wrapper.or().eq(PayBlack::getVal, val)));
        PayBlack black = payBlackMapper.selectOne(query);
        if (black != null) {
            return (black.getType() == 1 ? "黑名单:IP " : black.getType() == 2 ? "黑名单:邮箱 " : "") + black.getVal();
        }

        return null;
    }

    /**
     * 检查轮询组收款账号失败次数
     *
     * @param account    当前收款账号
     */
    private void checkGroupAccountFailNum(PayGroupAccount account) {
        try {
            //记录账号失败次数，并且限制
            if (account.getFailLimit() != null && account.getFailLimit() > 0) {
                redisAlgorithmKernel.addFailAccountNumToRedis(account.getId());

                Integer num = redisAlgorithmKernel.getFailAccountNum(account.getId());
                if (num >= account.getFailLimit()) {
                    //更新轮询组的账号下线
                    PayGroupAccount update = new PayGroupAccount();
                    update.setId(account.getId());
                    update.setStatus(4);
                    payGroupAccountService.updateById(update);

                    //更新账号失败
                    PayAccount up = new PayAccount();
                    up.setAccountId(account.getAccountId());
                    up.setStatus(3);
                    accountService.updateById(up);

                    redisAlgorithmKernel.delFailAccountNum(account.getId());
                }
            }
        }catch (Exception e){
            System.out.println("检查轮询组收款账号失败次数异常");
            e.printStackTrace();
        }
    }

    /**
     * 构建请求参数
     *
     * @param order 订单
     * @return
     */
    private JSONObject buildParams(PayGroupAccount groupAccount, PayAccount account, PayOrder order, Boolean isNewAccount) {

        StripeAuthBo sa = new StripeAuthBo();
        sa.setId(order.getOrderId());
        sa.setOrder_aid(order.getOrderAid());
        if (Objects.equals(groupAccount.getIsForce(), 1) && order.getForceCurrency() != null) {
            sa.setCurrency(order.getForceCurrency());
            sa.setOrder_money(order.getForceMoney().toPlainString());
        } else {
            sa.setCurrency(order.getCurrency());
            sa.setOrder_money(order.getCMoney().toPlainString());
        }
        sa.setCustomer_email(order.getCustomerEmail());
        sa.setFrom_domain(order.getFromDomain());
        sa.setIp(order.getFormIp());
        sa.setDomain(account.getDomain());
        sa.setRequest_domain(gatewayDomain);

        if (order.getGoodsType() == 1) {
            //实体订单
            sa.setCustomer_name(order.getCustomerName());
            //订单产品信息
            sa.setOrder_product(JSONArray.parseArray(order.getProduct(), OrderProduct.class));

            // 客户信息
            if (StringUtils.isNotEmpty(order.getCustomer())) {
                JSONObject customer = JSONObject.parseObject(order.getCustomer());
                StripeAuthBo.Address address = customer.to(StripeAuthBo.Address.class);
                address.setZip(customer.getString("postcode"));
                sa.setBilling(address);
            }
            // 收货信息
            if (StringUtils.isNotEmpty(order.getShipping())) {
                JSONObject shipping = JSONObject.parseObject(order.getShipping());
                StripeAuthBo.Address address = shipping.to(StripeAuthBo.Address.class);
                address.setZip(shipping.getString("postcode"));
                sa.setShipping(address);
            }
        } else {
            //虚拟订单
//            sa.setBilling(isNewAccount ? null : new StripeAuthBo.Address());
//            sa.setShipping(isNewAccount ? null : new StripeAuthBo.Address());
            sa.setBilling(null);
            sa.setShipping(null);
            //订单产品信息
            sa.setOrder_product(new ArrayList<>());
        }

        //设备信息
        if (StringUtils.isNotEmpty(order.getDevice())) {
            sa.setDevice(JSONObject.parseObject(order.getDevice(), AOrderBo.Device.class));
        } else {
            sa.setDevice(new AOrderBo.Device());
        }

        //付款信息
        StripeAuthBo.Payment payment = new StripeAuthBo.Payment();
        payment.setMethod(order.getPayMethod());
        payment.setType(Integer.valueOf(account.getApiType()));

        List<PayReceiveVo> receives = JSONArray.parseArray(groupAccount.getReceiveList(), PayReceiveVo.class);
        receives = receives.stream().peek(tmp -> {
            if (tmp.getSort() == null) {
                tmp.setSort(0);
            }
        }).sorted(Comparator.comparing(PayReceiveVo::getSort)).toList();

        payment.setMethod_types(receives.stream().map(PayReceiveVo::getCode).toList());

        //转成Map, 并且设置自定义字段
        Map<String, Object> paymentMap = payGroupTypeService.setCustomFields(MapstructUtils.convert(payment), groupAccount, account);

        //设置标题类型
        if (paymentMap.containsKey("titleType") && paymentMap.get("titleType") != null) {
            String var = paymentMap.get("titleType").toString();
            if (StringUtils.isNotEmpty(var)) {
                int type = Integer.parseInt(var);
                String customerName = StringUtils.isEmpty(order.getCustomerName()) ? StringUtils.generateRandomCode(new Random().nextInt(10, 18)) : order.getCustomerName();
                if (type == 3) {
                    paymentMap.put("title", customerName);
                }
                if (type == 4) {
                    paymentMap.put("title", paymentMap.getOrDefault("title", "") + "||" + customerName);
                }
            }
        }

        //支付标题 订单关键字替换
        String randomCode = StringUtils.generateRandomCode(new Random().nextInt(6, 15));
        if (paymentMap.containsKey("payTitle") && paymentMap.get("payTitle") != null) {
            paymentMap.remove("title");
            paymentMap.put("payTitle", replaceOrderField(paymentMap.get("payTitle") + "", order, randomCode));
        }
        //支付描述 订单关键字替换
        if (paymentMap.containsKey("describe") && paymentMap.get("describe") != null) {
            paymentMap.put("describe", replaceOrderField(paymentMap.get("describe") + "", order, randomCode));
        }

        sa.setPayment(paymentMap);

        return JSONObject.from(sa, JSONWriter.Feature.WriteMapNullValue);
    }

    /**
     * 插入订单
     *
     * @param app          应用信息
     * @param payType      支付平台
     * @param groupAccount 轮询组账号配置
     * @param account      收款账号
     * @param bo           订单参数
     * @param forceRates   强制汇率
     * @return 订单
     */
    private PayOrder insertPayOrder(PayApp app, PayType payType, PayGroupAccount groupAccount, PayAccount account, AOrderBo bo, BigDecimal forceRates, PayGroupTypeVo groupType) {

        //是否暂存订单
        boolean isTemp = Objects.equals(payType.getCode(), "adyen") && Objects.equals(groupAccount.getPayMethod(), 1);
        if(Objects.equals(payType.getCode(), "power")) {
            isTemp = true;
        }

        //是否随机邮箱
        boolean randomEmail = Objects.equals(payType.getCode(), "adyen") && Objects.equals(groupAccount.getPayMethod(), 2);
        if (randomEmail) {
            bo.getBilling().setEmail(SendEmailUtils.generateRandomEmail());
            bo.getShipping().setEmail(SendEmailUtils.generateRandomEmail());
        }

        //订单数据
        PayOrder order = new PayOrder();
        order.setAppId(app.getAppId());
        order.setClientId(app.getClientId());
        order.setClientName(app.getClientName());
        order.setAgentId(app.getAgentId());
        order.setAgentName(app.getAgentName());
        order.setManagerId(app.getManagerId());
        order.setManagerName(app.getManagerName());
        order.setSupplierId(account.getSupplierId());
        order.setSupplierName(account.getSupplierName());
        order.setGoodsType(app.getGoodsType());
        order.setPayTypeId(account.getPayTypeId());
        order.setOrderType(bo.getCreateLink() != null ? 2 : 1);
        order.setLinkCode(bo.getLinkCode());
        order.setOrderAid(bo.getOrderAid());
        order.setOrderNo(bo.getOrderNo());
        order.setCurrency(bo.getCurrency());//币别
        order.setRates(bo.getRates());//汇率
        //原始金额
        order.setAMoney(bo.getTotal());//原始金额
        order.setAUsdMoney(bo.getUsdMoney());//原始美元金额
        //订单金额
        order.setOrderMoney(bo.getTotal());
        order.setUsdMoney(bo.getUsdMoney());
        //C端订单金额
        order.setCMoney(bo.getTotal());
        order.setCUsdMoney(bo.getUsdMoney());

        //价格插件控制  20-100
        PricePluginKernel.setPriceV2(order, groupType);

        //强制货币
        if (Objects.equals(groupAccount.getIsForce(), 1) && forceRates != null) {
            order.setForceCurrency(groupAccount.getForceCurrency());
            order.setForceRate(forceRates);
            order.setForceMoney(order.getCUsdMoney().multiply(forceRates).setScale(2, RoundingMode.HALF_UP));
        }
        order.setFromDomain(bo.getFromDomain());
        order.setCustomerIp(Objects.equals(bo.getCreateLink(), 1) ? "none" : bo.getCustomerIpAddress());
        order.setFormIp(Objects.equals(bo.getCreateLink(), 1) ? "none" : bo.getCustomerIpAddress());
        order.setPayStatus(1);
        order.setStatus(isTemp ? -1 : 1);
        order.setTrackStatus(1);
        order.setIsFeign(0);
        order.setGroupId(groupAccount.getGroupId());
        order.setGroupAccountId(groupAccount.getId());
        order.setAccountId(account.getAccountId());
        order.setAccountName(account.getAccountName());
        order.setPayMethod(groupAccount.getPayMethod());

        //姓
        if (StringUtils.isEmpty(bo.getBilling().getFirstName())) {
            bo.getBilling().setFirstName("");
        }
        order.setCustomerName(bo.getBilling().getFirstName());

        //名
        if (StringUtils.isNotEmpty(bo.getBilling().getLastName())) {
            String name;
            if (StringUtils.isNotEmpty(bo.getBilling().getFirstName())) {
                name = order.getCustomerName() + " " + bo.getBilling().getLastName();
            } else {
                name = bo.getBilling().getLastName();
            }
            order.setCustomerName(name);
        }

        order.setCustomerPhone(bo.getBilling().getPhone());

        if (bo.getBilling().getAddress1() == null) {
            bo.getBilling().setAddress1("");
        }
        if (bo.getBilling().getAddress2() == null) {
            bo.getBilling().setAddress2("");
        }

        //客户邮箱
        order.setCustomerEmail(bo.getBilling().getEmail());
        //虚拟订单 是否传邮箱到支付平台 开关
        if (app.getGoodsType() == 2 && (app.getNeedMail() == null || app.getNeedMail() == 0)) {
            order.setCustomerEmail(null);
        }

        //计算产品小计金额
        List<OrderProduct> products = new ArrayList<>();
        for (AOrderBo.ProductData product : bo.getProductDataList()) {
            //使用汇率
            BigDecimal rate = Objects.equals(groupAccount.getIsForce(), 1) && order.getForceRate() != null ? order.getForceRate() : order.getRates();
            //计算单价
            BigDecimal price = product.getPrice().divide(rate, 2, RoundingMode.HALF_UP);

            StringBuilder mete_data = new StringBuilder();
            if (StringUtils.isNotEmpty(product.getAttributes())) {
                String attributes_data = product.getAttributes();
                String[] attributePairs = attributes_data.split(";");
                for (String attributePair : attributePairs) {
                    String[] attribute = attributePair.split(":");
                    if (attribute.length == 2) {
                        String k = attribute[0];
                        String val = attribute[1];
                        mete_data.append(k).append(":").append(val).append(" ");
                    }
                }
            }

            OrderProduct op = new OrderProduct();
            op.setPostId(product.getPostId());
            op.setName(product.getPostName());
            op.setPrice(price);
            op.setQuantity(product.getQuantity());
            op.setSubtotal(price.multiply(new BigDecimal(product.getQuantity())).setScale(2, RoundingMode.HALF_UP));
            op.setMete_data(String.valueOf(mete_data));
            op.setType(product.getType());
            op.setAttributes(product.getAttributes());
            op.setImage(product.getImage());
            products.add(op);
        }

        order.setCallbackUrl(bo.getCallbackUrl());
        order.setSuccessUrl(bo.getSuccessUrl());
        order.setCancelUrl(bo.getCancelUrl());
        order.setFailureUrl(bo.getFailureUrl());

        order.setGroupTypeRemark(groupType.getRemark());
        order.setIsDelete(0);
        order.setCreateTime(DateUtils.getNowDate());
        order.setIpCountry("");
        order.setSett(0);
        order.setCustomer(JSONObject.toJSONString(bo.getBilling(), JSONWriter.Feature.WriteMapNullValue));
        order.setShipping(JSONObject.toJSONString(bo.getShipping(), JSONWriter.Feature.WriteMapNullValue));
        order.setProduct(JSONArray.toJSONString(products, JSONWriter.Feature.WriteMapNullValue));
        order.setDevice(JSONObject.toJSONString(bo.getDevice(), JSONWriter.Feature.WriteMapNullValue));
        orderService.insertOrder(order);

        //异步隐藏相同邮箱订单
        if (hideOrder) {
            asyncHideEmailOrder(order);
        }

        //异步更新客户ip国家
//        asyncSetIpCountry(order);

        return order;
    }

    /**
     * 校验订单数据
     *
     * @param vo  订单参数
     * @param app 应用
     * @return 结果
     */
    private String verifyOrderData(AOrderBo vo, PayApp app) {

        if (StringUtils.isEmpty(vo.getOrderAid())) {
            return "Order number [order_id] cannot be empty";//订单号【order_id】不能为空
        }
        if (StringUtils.isEmpty(vo.getCurrency())) {
            return "Currency identifier [currency] cannot be empty";//货币标识【currency】不能为空
        }
        if (vo.getCreateLink() == null && StringUtils.isEmpty(vo.getFromDomain())) {
            return "Website domain name [from_domain] cannot be empty";//⽹站域名【from_domain】不能为空
        }
        if (vo.getTotal() == null) {
            return "Total order amount [total] cannot be empty";//订单总⾦额【total】不能为空
        }
        if (vo.getTotal().compareTo(BigDecimal.ZERO) <= 0) {
            return "Total order amount [total] cannot be less than or equal to 0";//订单总⾦额【total】不能小于等于0
        }

        if (vo.getBilling() == null) {
            return "Customer order information [billing] cannot be empty";//客户信息【billing】不能为空
        }
        AOrderBo.Address bill = vo.getBilling();
        if (vo.getCreateLink() == null && StringUtils.isEmpty(bill.getEmail())) {
            return "Customer email [email] cannot be empty";//客户邮箱【email】不能为空
        }

        if (vo.getShipping() == null) {
            return "Address information [shipping] cannot be empty";//送货信息【shipping】不能为空
        }
        AOrderBo.Address shipping = vo.getShipping();
        if (vo.getCreateLink() == null && StringUtils.isEmpty(shipping.getEmail())) {
            return "Address email [email] cannot be empty";//送货信息【email】不能为空
        }

        if (vo.getProductDataList() == null || vo.getProductDataList().isEmpty()) {
            return "Purchase product information [product_data] cannot be empty";//购买产品信息【product_data】不能为空
        }
        List<AOrderBo.ProductData> productData = vo.getProductDataList();

        if (vo.getCreateLink() == null && StringUtils.isEmpty(vo.getCustomerIpAddress())) {
            return "Customer order IP [customer_ip_address] cannot be empty";//客户下单IP【customer_ip_address】不能为空
        }

        if (StringUtils.isEmpty(bill.getCountry())) {
            return "Customer country [country] cannot be empty";//客户国家【country】不能为空
        }
        if (StringUtils.isEmpty(shipping.getCountry())) {
            return "Address country [country] cannot be empty";//送货信息【country】不能为空
        }

        if (app.getGoodsType() == 1) {
            //实体商品验证
            //客户地址信息
            if (StringUtils.isEmpty(bill.getFirstName())) {
                return "Customer name [first_name] cannot be empty";//客户名字【first_name】不能为空
            }
            if (StringUtils.isEmpty(bill.getLastName())) {
                return "Customer last name [last_name] cannot be empty";//客户姓【last_name】不能为空
            }

            if (vo.getProductDataList() == null && StringUtils.isEmpty(bill.getAddress1())) {
                return "Customer address 1 [address_1] cannot be empty";//收货详细地址1【address_1】不能为空
            }

            if (StringUtils.isEmpty(bill.getCity())) {
                return "Customer city [city] cannot be empty";//客户城市【city】不能为空
            }

            if (StringUtils.isEmpty(bill.getPostcode())) {
                return "Customer zip code [postcode] cannot be empty";//客户邮编【postcode】不能为空
            }

            if (StringUtils.isEmpty(bill.getPhone())) {
                return "Customer contact number [phone] cannot be empty";//客户联系电话【phone】不能为空
            }
            //送货地址信息
            if (StringUtils.isEmpty(shipping.getFirstName())) {
                return "Address name [first_name] cannot be empty";//客户名字【first_name】不能为空
            }
            if (StringUtils.isEmpty(shipping.getLastName())) {
                return "Address last name [last_name] cannot be empty";//客户姓【last_name】不能为空
            }

            if (vo.getProductDataList() == null && StringUtils.isEmpty(shipping.getAddress1())) {
                return "Address address 1 [address_1] cannot be empty";//收货详细地址1【address_1】不能为空
            }

            if (StringUtils.isEmpty(shipping.getCity())) {
                return "Address city [city] cannot be empty";//客户城市【city】不能为空
            }

            if (StringUtils.isEmpty(shipping.getPostcode())) {
                return "Address zip code [postcode] cannot be empty";//客户邮编【postcode】不能为空
            }

            if (StringUtils.isEmpty(shipping.getPhone())) {
                return "Address contact number [phone] cannot be empty";//客户联系电话【phone】不能为空
            }

            for (AOrderBo.ProductData product : productData) {
                if (StringUtils.isEmpty(product.getPostName())) {
                    return "product name [post_name] cannot be empty";//产品名称【post_name】不能为空
                }

                if (product.getQuantity() == null) {
                    return "Purchase quantity [quantity] cannot be empty";//购买数量【quantity】不能为空
                }

                if (product.getPrice() == null || product.getPrice().equals(BigDecimal.ZERO)) {
                    return "Product unit price [price] cannot be empty and must be greater than 0";//产品单价【price】不能为空，并且要大于0
                }

                if (StringUtils.isEmpty(product.getType())) {
                    return "product type [type] cannot be empty";//产品类型【type】不能为空
                }

                if (product.getType().equals("variable")) {
                    if (StringUtils.isEmpty(product.getAttributes())) {
                        return "When type=variable, the product attribute value [attributes] cannot be empty.";//type=variable时，产品属性值【attributes】不能为空
                    }
                }
            }

        } else {
            //虚拟商品验证
            for (AOrderBo.ProductData product : productData) {
                if (product.getQuantity() == null) {
                    return "Purchase quantity [quantity] cannot be empty";//购买数量【quantity】不能为空
                }

                if (product.getPrice() == null || product.getPrice().equals(BigDecimal.ZERO)) {
                    return "Product unit price [price] cannot be empty and must be greater than 0";//产品单价【price】不能为空，并且要大于0
                }
            }
        }

        if (StringUtils.isEmpty(vo.getCallbackUrl()) && vo.getCreateLink() == null) {
            return "Asynchronous callback address [callback_url] cannot be empty";//异步回调地址【callback_url】不能为空
        }
        if (StringUtils.isEmpty(vo.getCancelUrl())) {
            return "Jump address for Cancel payment [cancel_url] cannot be empty";//取消⽀付跳转地址【cancel_url】不能为空
        }
        if (StringUtils.isEmpty(vo.getFailureUrl())) {
            return "Jump address for payment failure [failure_url] cannot be empty";//⽀付失败跳转地址【failure_url】不能为空
        }
        if (StringUtils.isEmpty(vo.getSuccessUrl())) {
            return "Jump address if payment is successful [success_url] cannot be empty";//⽀付成功跳转地址【success_url】不能为空
        }
        return null;
    }

    /**
     * 校验订单数据
     *
     * @param vo      订单参数
     * @param payType 支付平台
     * @return 结果
     */
    private String verifyOrderDataV2(AOrderBo vo, PayType payType) {

        if (payType.getCode().equals("squirtle")) {
            if (vo.getDevice() == null) {
                return "Device information [device] cannot be empty";//设备信息【device】不能为空
            }
            AOrderBo.Device device = vo.getDevice();

            if (StringUtils.isEmpty(device.getOs())) {
                return "Device os cannot be empty";
            }
            if (StringUtils.isEmpty(device.getBrowser())) {
                return "Device browser cannot be empty";
            }
            if (StringUtils.isEmpty(device.getBrowserLang())) {
                return "Device browserLang cannot be empty";
            }
            if (StringUtils.isEmpty(device.getTimeZone())) {
                return "Device timeZone cannot be empty";
            }
            if (StringUtils.isEmpty(device.getResolution())) {
                return "Device resolution cannot be empty";
            }
            if (StringUtils.isEmpty(device.getIsCopyCard())) {
                return "Device isCopyCard cannot be empty";
            }

//            for (AOrderBo.ProductData product : vo.getProductDataList()) {
//                if (product.getPostId() == null) {
//                    return "product post_id [post_id] cannot be empty";
//                }
//            }
        }

        return null;
    }

    /**
     * 异步更新获取客户ip的国家
     *
     * @param payOrder 订单
     */
    private void asyncSetIpCountry(PayOrder payOrder) {
        executor.submit(() -> {
            String ipInfo = getCustomerIpCountry(payOrder.getCustomerIp());
            if (StringUtils.isNotEmpty(ipInfo)) {
                try {
                    if (ipInfo.equals("fail")) {
                        Thread.sleep(1000);
                        ipInfo = getCustomerIpCountry(payOrder.getCustomerIp());
                    }
                    if (ipInfo.equals("fail")) {
                        Thread.sleep(1000);
                        ipInfo = getCustomerIpCountry(payOrder.getCustomerIp());
                    }

                    JSONObject ipJson = JSONObject.parseObject(ipInfo);
                    if (ipJson == null) {
                        return;
                    }

                    String countryCode = null;
                    if (ipJson.containsKey("visitor")) {
                        JSONObject visitorObj = ipJson.getJSONObject("visitor");
                        if (visitorObj.containsKey("geo")) {
                            JSONObject geoObj = visitorObj.getJSONObject("geo");
                            if (geoObj.containsKey("countryCode")) {
                                countryCode = geoObj.getString("countryCode");
                            }
                        }
                    }
                    if (StringUtils.isNotEmpty(countryCode)) {
                        PayOrder upObj = new PayOrder();
                        upObj.setOrderId(payOrder.getOrderId());
                        upObj.setIpCountry(countryCode);
                        orderService.updatePayOrder(upObj);
                    }
                } catch (Exception ignore) {
                }
            }
        });
    }

    /**
     * 异步隐藏相同邮箱订单
     *
     * @param order 订单
     */
    private void asyncHideEmailOrder(PayOrder order) {
        if (StringUtils.isEmpty(order.getCustomerEmail())) {
            return;
        }
        executor.submit(() -> {
            LambdaUpdateWrapper<PayOrder> query = new LambdaUpdateWrapper<>();
            query.ne(PayOrder::getOrderId, order.getOrderId());//排除当前订单
            query.eq(PayOrder::getClientId, order.getClientId());//该商户的订单
            query.eq(PayOrder::getCustomerEmail, order.getCustomerEmail());//相同邮箱的
            query.in(PayOrder::getStatus, 1, 3, 4);//已创建、支付失败、高风险
            query.eq(PayOrder::getIsFeign, 0);//不是吃单的
            query.ge(PayOrder::getCreateTime, DateUtils.getDate() + " 00:00:00");//查询今日的订单
            query.set(PayOrder::getHide, 1);//改为隐藏
            orderService.updatePayOrder(query);
        });
    }

    private String getCustomerIpCountry(String ip) {
        try {
            return new RequestUtils().getIpContry(ipCountryUrl, ipCountryKey, ip, RequestUtils.USER_AGENT);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 生成B端订单号
     *
     * @return 订单号
     */
    public String generateOrderNumber() {
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
        int randomNum = new Random().nextInt(1000); // 生成一个三位的随机数
        return timestamp + String.format("%03d", randomNum);
    }

    /**
     * 重试设置收款账号
     *
     * @param bo  订单参数
     * @param app 应用
     * @return 结果
     */
    public R<?> loopGroupAccount(AOrderBo bo, PayApp app) {
        if (StringUtils.isEmpty(bo.getPayType())) {
            return R.fail("PayType is invalid");
        }
        if (app.getGroupId() == null) {
            return R.fail("No bind pay group");
        }
        //获取应用配置的支付平台
        PayGroupTypeQueryBo pbo = new PayGroupTypeQueryBo(bo.getTotal().toPlainString(), bo.getCurrency(), bo.getBilling().getCountry(), bo.getUsdMoney());
        R<List<PayTypeOutVo>> r = payGroupTypeService.selectPayGroupType(app.getGroupId(), bo.getPayType(), pbo);
        if (R.isError(r)) {
            return R.fail(r.getMsg());
        }

        List<PayTypeOutVo> outVos = r.getData();
        bo.setGroupAccountId((Long) outVos.get(0).getAccount().get("id"));
        return R.ok(outVos);
    }

    /**
     * 获取轮询组账号
     *
     * @param bo  订单参数
     * @param app 应用
     * @return 结果
     */
    public R<PayOrderInsertAccountVo> getGroupAccount(AOrderBo bo, PayApp app) {

        //应用账号配置
        PayGroupAccount groupAccount = payGroupAccountService.queryById(bo.getGroupAccountId());
        if (groupAccount == null) {
            return R.fail("GroupAccount is invalid");
        }

        //查询轮询组收款平台配置
        PayGroupTypeVo typeVo = payGroupTypeMapper.selectVoById(groupAccount.getGroupTypeId());
        if (typeVo == null) {
            return R.fail("No pay group types");
        }

        boolean hasEmailOrIp = StringUtils.isNotEmpty(bo.getBilling().getEmail()) || (StringUtils.isNotEmpty(bo.getCustomerIpAddress()) && Objects.equals(bo.getCustomerIpAddress(), "none"));

        String key = null;
        if (Objects.equals(typeVo.getFixedType(), 1)) {
            key = bo.getBilling().getEmail();
        }
        else if (Objects.equals(typeVo.getFixedType(), 2)) {
            key = bo.getCustomerIpAddress();
        }

        boolean isFixed = hasEmailOrIp && StringUtils.isNotEmpty(key) && Objects.equals(typeVo.getIsFixed(), 1) && Objects.equals(typeVo.getIsLoop(), 1);//是否开启防止关联
        String fixedKey = OrderConstants.pay_fixed_account + typeVo.getId() + ":" + key;
        boolean isOldAct = false;//是否防关联账号  这种账号不校验是否下线
        if (isFixed && RedisUtils.hasKey(fixedKey)) {
            Object groupActId = RedisUtils.getCacheObject(fixedKey);
            if (!Objects.equals(groupAccount.getId(), groupActId)) {
                PayGroupAccount oldAct = payGroupAccountService.queryById(Long.valueOf(groupActId.toString()));
                //账号存在，并且没有禁用
                if (oldAct != null && !Objects.equals(oldAct.getStatus(), 3)) {
                    groupAccount = oldAct;
                    isOldAct = true;
                }
            }
        }

        if (!isOldAct && !Objects.equals(groupAccount.getStatus(), 1)) {
            return R.fail("GroupAccount is offline");
        }

        //是不是该应用所对应轮询组的账号
        if (!Objects.equals(app.getGroupId(), groupAccount.getGroupId())) {
            return R.fail("The account not this app");
        }

        //收款账号
        PayAccount account = accountService.selectById(groupAccount.getAccountId());
        if (account == null) {
            return R.fail("Account is invalid");
        }
        if (isOldAct) {
            //如果是防关联账号, 则只校验账号是否禁用
            if (Objects.equals(account.getStatus(), 3)) {
                return R.fail("Account is disabled");
            }
        } else {
            //校验账号是否在线
            if (!Objects.equals(account.getStatus(), 1)) {
                return R.fail("Account is down");
            }
        }

        //获取支付平台
        PayType payType = payTypeMapper.selectById(account.getPayTypeId());
        if (payType == null) {
            return R.fail("PayType is invalid");//支付平台不可用
        }

        //获取API配置
        ApiConfig config = ApiConfig.getConfig(payType.getConfig(), account.getApiType());
        //获取支付方式配置
        ApiConfig.PayMethod method = ApiConfig.getPayMethod(config, groupAccount.getPayMethod());


        //v1接口，只有收银台模式可以使用防关联，内嵌模式无法使用防关联，因为内嵌链接是提前加载的，在此更换账号会导致账号不一致
        if (method != null && method.getInnerType() == null && isFixed && !RedisUtils.hasKey(fixedKey)) {
            //防关联账号  邮箱/ip绑定收款平台的一个收款账号，缓存一小时
            RedisUtils.setCacheObject(fixedKey, groupAccount.getId(), Duration.ofHours(fixedKeyTime));
        }

        //组装数据
        PayOrderInsertAccountVo vo = new PayOrderInsertAccountVo(typeVo, groupAccount, account, payType);
        if(isFixed) {
            vo.setFixedKey(fixedKey);
        }
        return R.ok(vo);
    }

    /**
     * 替换订单字段
     * @param str 字符串
     * @param order 订单
     * @return 结果
     */
    public String replaceOrderField(String str, PayOrder order, String randomCode) {
        if (StringUtils.isEmpty(str)) {
            return str;
        }

        AOrderBo.Address shipping = null;
        try {
            shipping = JSONObject.parseObject(order.getShipping(), AOrderBo.Address.class);
        } catch (Exception ignore) {
        }

        // [随机值]  [下单邮箱]  [客户姓名]  [客户电话]  [客户邮编]  [国家简码]
        if (str.contains("[随机值]")) {
            str = str.replace("[随机值]", randomCode);
        }

        if (str.contains("[下单邮箱]")) {
            str = str.replace("[下单邮箱]", StringUtils.isEmpty(order.getCustomerEmail()) ? "" : order.getCustomerEmail());
        }
        if (str.contains("[客户姓名]")) {
            str = str.replace("[客户姓名]", StringUtils.isEmpty(order.getCustomerName()) ? "" : order.getCustomerName());
        }
        if (str.contains("[客户电话]")) {
            str = str.replace("[客户电话]", StringUtils.isEmpty(order.getCustomerPhone()) ? "" : order.getCustomerPhone());
        }
        if (str.contains("[客户邮编]")) {
            str = str.replace("[客户邮编]", shipping == null ? "" : shipping.getEmail());
        }
        if (str.contains("[国家简码]")) {
            str = str.replace("[国家简码]", shipping == null ? "" : shipping.getCountry());
        }

        return str;
    }
}
